package jcataloger.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import jcataloger.beans.FileDocument;
import jcataloger.beans.ImageItem;
import jcataloger.beans.SearchResultBean;
import jcataloger.gui.JCatalogerParams;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermEnum;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;

/**
 * This class makes index operations in the project (i.e. adding new image to index, deleting an
 * image from the index, searching in the index). Searching facilities are performed in here.   
 * @author sekobey
 *
 */
public class Indexer implements JCatalogerParams {

	// Singleton pattern is used.
	private static Indexer instance;
	private File indexDir = null;
	// writing to the index is performed by this object
	private IndexWriter writer;
	
	private Indexer() {
		try {
			indexDir = new File(INDEX_DIR);
			if (indexDir.exists())
				deleteIndexDir();
			if ( IndexReader.indexExists(indexDir) ) {
				writer = new IndexWriter(indexDir, new StandardAnalyzer(), false);
			}
			else
				writer = new IndexWriter(indexDir, new StandardAnalyzer(), true);
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Opens the index.
	 * @return an Indexer object.
	 */
	public static Indexer openIndex() {
		if ( instance == null )
			instance = new Indexer();
		return instance;
	}
	
	/**
	 * Closes the index.
	 *
	 */
	public void closeIndex() {
		try {
			writer.close();
			deleteIndexDir();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * When a file is opened, it must be merged with the existing index.
	 *
	 */
	public void merge() {
		try {
			IndexReader reader = IndexReader.open(indexDir);
			writer.addIndexes(new IndexReader[]{ reader });
			reader.close();
		} catch (IOException e) {
			
		}
	}
	
	
	/**
	 * Removes all images from the index.
	 */
	public void deleteAllDiskImages() {
		try {
			if (writer != null ) IndexReader.unlock(writer.getDirectory());
			IndexReader reader = IndexReader.open(indexDir);
			TermEnum termEnum = reader.terms();
			while (termEnum.next()) {
				Term term = termEnum.term();
				if (term.field().equals("diskName")) {
					reader.deleteDocuments(term);
				}
				else
					break;
			}
			reader.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	/**
	 * Adds an image to the index.
	 * @param diskName shows the name of the image. diskName field of the index is set with it.
	 * @param directoryMap shows the directory structure of the image. Other fields of the index is set by
	 * this parameter.
	 */
	public void addDiskImage(String diskName, Map<Integer, List<ImageItem>> directoryMap) {
		Set<Entry<Integer, List<ImageItem>>> entries = directoryMap.entrySet();
		for ( Entry<Integer,List<ImageItem>> entry : entries ) {
			List<ImageItem> dirContents = entry.getValue();
			for (ImageItem item : dirContents) {
				try {
					Document document = FileDocument.document(diskName, item);
					writer.addDocument(document);
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		try {
			writer.optimize();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * Removes a disk image from the index
	 * @param diskName removing the image from the index is done due to the name of the image.
	 * @return number of removed items.
	 */
	public int deleteDiskImage(String diskName) {
		Term t = new Term(FileDocument.FIELDS.diskName.name(), diskName);
		int numOfRecords = 0;
		try {
			IndexReader.unlock(writer.getDirectory());
			IndexReader reader = IndexReader.open(indexDir);
			reader.deleteDocuments(t);
			reader.close();
			writer.optimize();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return numOfRecords;
	}
	
	/**
	 * Searchs a text in the index.
	 * @param text the search text.
	 * @param matchCase if you want the case sensitivity in search, set this parameter true.
	 * @return results in SearchResultBean
	 */
	public List<SearchResultBean> searchText(String text, boolean matchCase) {
		List<SearchResultBean> list = new ArrayList<SearchResultBean>();
		String fileNameField = FileDocument.FIELDS.fileName.name();
		String fileNameLowerField = FileDocument.FIELDS.fileNameLower.name();
		IndexReader reader = null;
		try {
			reader = IndexReader.open(writer.getDirectory());
			Searcher searcher = new IndexSearcher(reader);
			Analyzer analyzer = new StandardAnalyzer();
			QueryParser parser = null;
			if ( !matchCase ) {
				parser = new QueryParser(fileNameLowerField, analyzer);
				parser.setLowercaseExpandedTerms(true);
			} else {
				parser = new QueryParser(fileNameField, analyzer);
				parser.setLowercaseExpandedTerms(false);
			}
			text += "*";
			Query query = parser.parse(text);
			Debug.println("Query:"+query.toString());
			Hits hits = searcher.search(query);
			for (int i = 0; i < hits.length(); i++) {
				Document doc = hits.doc(i);
				SearchResultBean bean = new SearchResultBean(doc.get("diskName"),
												doc.get("fileName"),
												doc.get("path"),
												doc.get("type"),
												doc.get("length"),
												doc.get("extension"));
				list.add(bean);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (reader != null )
				try {
					reader.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
		
		return list;
		
	}

	// deletes the index directory from the user's computer.
	private void deleteIndexDir() {
		File[] files = indexDir.listFiles();
		for (File f:files) {
			f.delete();
		}
		indexDir.delete();
	}
	
	
}
